package org.datascooter.utils;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.impl.Condition;
import org.datascooter.impl.DataSnip;
import org.datascooter.impl.LogicValue;
import org.datascooter.impl.Snip;
import org.datascooter.utils.policy.SnipType;

public class SnipUtils {

	public static final String NL = "\n";
	public static final String NN = "";
	public static final String SP = " ";
	public static final String COLON = ":";
	public static final String ASTERICS = "*";
	public static final String COMMA = " , ";
	public static final String DOT = ".";
	public static final String DOT_ASTERICS = ".*";
	public static final String EQ = " = ";
	public static final String EQQU = " = ? ";
	public static final String SEMICOLON = ";";
	public static final String UNDERLINE = "_";
	public static final String RB = ") ";
	public static final String QU = " ? ";
	public static final String LB = " (";
	public static final String LBQURB = " (?) ";
	public static final String ZERO = "0";
	//
	public static final String NOT = "NOT";
	public static final String AND = " AND ";
	public static final String ASC = "ASC";
	public static final String DESC = "DESC";
	public static final String FROM = "FROM";
	public static final String BLOB = "BLOB";
	public static final String CHAR = "CHAR";
	public static final String CLOB = "CLOB";
	public static final String DISTINCT = " DISTINCT ";
	public static final String IS_NOT_NULL = "IS NOT NULL";
	public static final String IS_NULL = "IS NULL";
	public static final String JOIN = " {0}.{1} = {2}.{3} ";
	public static final String NOT_NULL = " NOT NULL ";
	public static final String NULL = " NULL ";
	public static final String NUMBER = "NUMBER";
	public static final String ORDER_BY = " ORDER BY ";
	public static final String TIMESTAMP = "TIMESTAMP";
	public static final String UNIQUE = " UNIQUE ";
	public static final String VARCHAR = "VARCHAR";
	public static final String WHERE = " WHERE ";
	//
	public static final String CREATE_TABLE = "CREATE TABLE {0} (\n{1}\n)";
	public static final String DROP_TABLE = "DROP TABLE {0}";
	public static final String ALTER_TABLE_ADD = "ALTER TABLE {0} ADD {1}";
	public static final String ALTER_TABLE_PK = "ALTER TABLE {0} ADD CONSTRAINT {1}  PRIMARY KEY ({2})";
	public static final String ALTER_TABLE_CHANGE = "ALTER TABLE {0} ALTER COLUMN {1}";
	public static final String FK = "ALTER TABLE {0}  ADD CONSTRAINT FK_{1} FOREIGN KEY  ({2}) REFERENCES {3} ({4}) ";// ON DELETE CASCADE
	public static final String ALTER_TABLE = "ALTER TABLE {0} ADD CONSTRAINT PK_{1}_{2} PRIMARY KEY ({3})";
	public static final String DROP_FK = "ALTER TABLE {0} DROP CONSTRAINT {1}";
	public static final String CREATE_INDEX = "CREATE INDEX IDX_{0} {1} ON {2}({3})";
	public static final String FOREIGN_KEY = " FOREIGN KEY ({0}) REFERENCES {1}({2})";
	public static final String CREATE_INDEX_COPY = "CREATE INDEX {0} ON {1}({2})";
	//
	public static final String INSERT_INTO = "INSERT INTO {0} ({1}) VALUES({2})";
	public static final String SELECT_FROM = "SELECT {0} {1} FROM {2} {3}";
	public static final String UPDATE_WHERE = "UPDATE {0} SET {1} {2}";
	public static final String DELETE_FROM = "DELETE  FROM {0} {1} {2}";
	public static final String DELETE_FROM1 = "DELETE {0} FROM  {1} {2}";

	public static final String snipToString(Snip snip) {
		StringBuilder builder = buildString(0, snip);
		Snip[] fetch = snip.getFetch();
		if (fetch != null && fetch.length > 0) {
			buildfetch(1, builder, fetch);
		}
		builder.append(" \n</snip>\n");
		return builder.toString();
	}

	public static final String snipToString(int level, Snip snip) {
		StringBuilder builder = buildString(level + 0, snip);
		Snip[] fetch = snip.getFetch();
		if (fetch != null && fetch.length > 0) {
			buildfetch(level + 1, builder, fetch);
			builder.append(SnipUtils.NL);
			builder.append(LangUtils.repeat(SP, level));
		}
		builder.append("</snip>");
		return builder.toString();
	}

	private static StringBuilder buildString(int level, Snip snip) {
		String paramsStr = NULL;
		if ((snip.getData() != null) && (snip.getData().length > 0) && (snip.getData()[0] != null)) {
			paramsStr = Arrays.toString(snip.getData()[0]);
		}
		StringBuilder builder = new StringBuilder();
		if (level == 0) {
			builder.append(SnipUtils.NL);
		}
		builder.append(LangUtils.repeat(SP, level));
		builder.append("<snip> Entity:  " + snip.entity + "  Type: " + snip.type + " Query:  " + snip.query + "  Params: " + paramsStr
			+ "   Fields: " + Arrays.toString(snip.fields) + "  Offset/Limit: " + snip.fromRow + " / " + snip.fetchSize);
		return builder;
	}

	private static void buildfetch(int level, StringBuilder builder, Snip[] fetch) {
		builder.append(SnipUtils.NL);
		builder.append(LangUtils.repeat(SP, level));
		builder.append("<fetch>");
		for (int a = 0; a < fetch.length; a++) {
			builder.append(SnipUtils.NL);
			builder.append(LangUtils.repeat(SP, level + 1));
			builder.append(snipToString(level + 2, fetch[a]).trim());
		}
		builder.append(SnipUtils.NL);
		builder.append(LangUtils.repeat(SP, level));
		builder.append("</fetch>");
	}

	public static final String snipToSQLString(Snip snip) {
		String paramsStr = NULL;
		if ((snip.getData() != null) && (snip.getData().length > 0) && (snip.getData()[0] != null)) {
			paramsStr = Arrays.toString(snip.getData()[0]);
		}
		return "  " + snip.query + "  Param: " + paramsStr + "  Offset/Limit: " + snip.fromRow + " / " + snip.fetchSize;
	}

	public static final String getContext(String url) {
		if (url != null) {
			int indexOf = url.indexOf(COLON);
			if (indexOf >= 0) {
				String substring = url.substring(indexOf + 1, url.length());
				indexOf = substring.indexOf(COLON);
				if (indexOf >= 0) {
					return substring.substring(0, indexOf).toLowerCase();
				}
			}
		}
		return url;
	}

	@SuppressWarnings("nls")
	public static DataSnip getSnipFromDataSet(String entity, Object[] fields) throws EntityNotMappedException, SecurityException, NoSuchMethodException {
		DataSnip result = new DataSnip(entity, SnipType.NONE);
		EntityBundle bundle = DSMapper.getBundle(entity);
		if (bundle != null) {
			if (fields.length == 1 && fields[0].getClass().isArray()) {
				fields = (Object[]) fields[0];
			}
			int index = 0;
			for (PersistAttribute attribute : bundle.attrArray) {
				result.what(attribute.name, fields[index++]);
			}
		}
		return result;
	}

	@SuppressWarnings("nls")
	public static String toStringInt(DataSnip snip, int level) {
		StringBuilder builder = new StringBuilder();
		builder.append(NL);
		builder.append(LangUtils.repeat(SP, level + 1));
		builder.append(snip.getSnipType());
		builder.append(SP);
		builder.append(snip.entityName);
		if (snip.parentId != null) {
			builder.append("   parent=");
			builder.append(snip.parentId);
		}
		if (snip.where != null) {
			builder.append(SP);
			builder.append(LangUtils.repeat(SP, level + 2));
			builder.append("where: ");
			builder.append(snip.where);
		}
		if (snip.what != null) {
			builder.append(SP);
			builder.append(LangUtils.repeat(SP, level + 2));
			builder.append("set:  ");
			builder.append(snip.what);
		}
		if (snip.joinList.size() > 0) {
			builder.append(NL);
			builder.append(LangUtils.repeat(SP, level + 2));
			builder.append("Join:");
			for (DataSnip child : snip.joinList) {
				builder.append(NL);
				builder.append(LangUtils.repeat(SP, level + 3));
				builder.append(toStringInt(child, level + 1));
			}
		}
		if (snip.fetchList.size() > 0) {
			builder.append(NL);
			builder.append(LangUtils.repeat(SP, level + 2));
			builder.append("Fetch:");
			for (DataSnip child : snip.fetchList) {
				builder.append(NL);
				builder.append(LangUtils.repeat(SP, level + 3));
				builder.append(toStringInt(child, level + 1));
			}
		}
		builder.append(NL);
		return builder.toString();
	}

	public static int buildHash(DataSnip snip) {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((snip.entityName == null) ? 0 : snip.entityName.hashCode());
		result = prime * result + ((snip.getSnipType() == null) ? 0 : snip.getSnipType().hashCode());
		result = prime * result + ((snip.getFunction() == null) ? 0 : snip.getFunction().hashCode());
		result = prime * result + ((snip.sqlClause == null) ? 0 : snip.sqlClause.hashCode());
		result = prime * result + ((snip.joinList == null) ? 0 : snip.joinList.hashCode());
		result = prime * result + ((snip.what == null) ? 0 : snip.what.hashCode());
		result = prime * result + ((snip.where == null) ? 0 : snip.where.hashCode());
		result = prime * result + ((snip.extraList == null) ? 0 : snip.extraList.hashCode());
		result = prime * result + ((snip.extraAttribute == null) ? 0 : snip.extraAttribute.hashCode());
		result = prime * result + ((snip.getChildPolicy() == null) ? 0 : snip.getChildPolicy().hashCode());
		result = prime * result + ((snip.excludeExtraList == null) ? 0 : snip.excludeExtraList.hashCode());
		result = prime * result + snip.parentIndex;
		result = prime * result + ((snip.fetchList == null) ? 0 : snip.fetchList.hashCode());
		result = prime * result + ((snip.fromRow == 1) ? 0 : 32);
		result = prime * result + ((snip.fetchSize == 0) ? 0 : 33);
		return result;
	}

	public static void hookValue(DataSnip snip, Object... sourceObject) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		hookValueInt(snip, snip.where, sourceObject);
		for (DataSnip child : snip.joinList) {
			hookValue(child, sourceObject);
		}
	}

	private static void hookValueInt(DataSnip snip, Condition condition, Object... sourceObject) throws EntityNotMappedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		if (condition != null) {
			for (LogicValue logicValue : condition.valueList) {
				ValueHook hook = logicValue.hook;
				if (hook != null) {
					PersistAttribute attribute = null;
					if (LangUtils.isEmptyString(hook.attribute)) {
						attribute = DSMapper.getBundle(LangUtils.defaultIfEmpty(hook.entity, snip.entityName)).id;
					} else {
						attribute = DSMapper.getBundle(LangUtils.defaultIfEmpty(hook.entity, snip.entityName)).getAttribute(hook.attribute);
					}
					logicValue.setValue(attribute.getValue(sourceObject[hook.order]));
				}
			}
			for (Condition child : condition.childList) {
				hookValueInt(snip, child, sourceObject);
			}
		}
	}

	public static void rebuildId(DataSnip dataSnip) {
		dataSnip.rebuildId();
		for (DataSnip snip : dataSnip.fetchList) {
			snip.rebuildId();
		}
		for (DataSnip snip : dataSnip.joinList) {
			snip.rebuildId();
		}
		if (dataSnip.what != null) {
			dataSnip.what.rebuildId();
			dataSnip.whatId = dataSnip.what.getId();
			dataSnip.what.snipId = dataSnip.getId();
		}
		if (dataSnip.where != null) {
			dataSnip.where.rebuildId();
			dataSnip.whereId = dataSnip.where.getId();
			dataSnip.where.snipId = dataSnip.getId();
		}
	}
}
